package com.yc.sole.system.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSONUtil;
import com.yc.sole.common.domain.UserInfo;
import com.yc.sole.common.enums.TrueOrFalse;
import com.yc.sole.framework.context.SecureContextHolder;
import com.yc.sole.framework.utils.IpUtil;
import com.yc.sole.framework.web.PreAuth;
import com.yc.sole.system.dto.LogDto;
import com.yc.sole.system.repository.LogRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.PostMapping;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author yizuomin
 * @date 2023/2/27 15:17
 **/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
@Order(1)
public class LogAspect {
    private final LogRepository logRepository;
    private final PathMatcher pathMatcher;
    private final HttpServletRequest request;

    @Around("@annotation(com.yc.sole.framework.web.PreAuth)")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String startTime = DateUtil.now();
        Signature signature = joinPoint.getSignature();
        MethodSignature msg = (MethodSignature) signature;
        Object target = joinPoint.getTarget();
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        //通过方法获取注解
        PreAuth preAuth = method.getAnnotation(PreAuth.class);
        PostMapping annotation = method.getAnnotation(PostMapping.class);
        Object proceed;
        boolean error = false;
        String errorMessage = "";
        try {
            proceed = joinPoint.proceed();
        } catch (Exception e) {
            error = true;
            errorMessage = e.getMessage();
            throw e;
        } finally {
            stopWatch.stop();
            //记录日志
            UserInfo userInfo = (UserInfo) SecureContextHolder.getUserInfo();
            String[] value = preAuth.value();
            boolean match = false;
            for (String s : value) {
                match = pathMatcher.match("**:**:select", s);
            }
            long lastTaskTimeMillis = stopWatch.getLastTaskTimeMillis();
            if (Objects.nonNull(userInfo) && !match && preAuth.check()) {
                Object[] args = joinPoint.getArgs();
                LogDto logDto = new LogDto();
                logDto.setUserId(userInfo.getId());
                logDto.setUsername(userInfo.getUsername());
                logDto.setIp(IpUtil.getIpAddress(request));
                IpUtil.IPInfo region = IpUtil.getRegion(logDto.getIp());
                logDto.setAddress(region.getAddress() + "_" + region.getIsp());
                logDto.setUri(annotation.value()[0]);
                logDto.setRequestTime(startTime);
                logDto.setMethodName(preAuth.description());
                logDto.setExecutionTime(lastTaskTimeMillis);
                logDto.setRequest(ArrayUtil.isNotEmpty(args) ? CharSequenceUtil.sub(JSONUtil.toJsonStr(args.length > 1 ? args : args[0]), 0, 1000) : null);
                logDto.setErrorFlag(error ? TrueOrFalse.TRUE : TrueOrFalse.FALSE);
                logDto.setErrorMessage(CharSequenceUtil.sub(errorMessage, 0, 3000));
                logRepository.saveLog(logDto);
            }
            log.info("{} use {} millis", request.getRequestURI(), lastTaskTimeMillis);
        }
        // Thread.sleep(500);
        return proceed;
    }
}
